import pandas as pd
import datetime
import numpy as np
import re
import gc


def INTPresent(df, colName):  # 计算变化率
    """
    同列增长百分比
    观察值得变化率与目标值变化率关系
    """
    NewName = "{}_by%".format(colName)
    df[NewName] = round(
        ((df.loc[:, colName] - df.loc[:, colName].shift(1)) / df.loc[:, colName]), 5
    )

    return df, NewName


def direction(df, colName):  # 数值转布尔
    """
    获取数值方向， x > 0 == 1, x < 0 == -1
    """
    NewName = "{}_Boll".format(colName)
    df[NewName] = df[colName].apply(lambda x: 1 if x > 0 else (-1 if x < 0 else np.nan))

    return df, NewName


def A_B(df, colA, colB):  # df['a'] - df['b']
    """
    表格中 A-B取差值
    观察值得变化与目标值关系
    """
    NewName = "{}-{}".format(
        colB,
        colA,
    )
    df.loc[:, NewName] = df.loc[:, colA] - df.loc[:, colB]
    return df, NewName


def ChangeBoll(df, key):  # 变化率布尔值
    """
    生成自平移线
    """
    df, key = INTPresent(df, key)
    df, newname = direction(df, key)
    df = df.drop(key, axis=1)
    return df, newname


def Macount(df, day_list):
    temp_list = []
    a = 0
    prescale = 0
    for num in day_list:
        col = "Avg_ma_" + str(num)
        temp_list.append(col)
    linecount = pd.DataFrame(df[temp_list].notna().sum(axis=1), columns=["count"])
    df = pd.merge(df, linecount, left_index=True, right_index=True)
    return df


def MAsBuliderMAIN(df, KEYLIST, MA_LIST, shift=True):  # 生成各种趋势线 MA, MA_BOLL ,MA-MAIN()
    """
    - 将股票数据转为
    - df = pandas.DataFrame
    - KEYLIST = 键值
    - MA_LIST = MA线段的各个值
    """
    a = 0
    prescale = 0
    start = datetime.datetime.now()
    print("MAsBuliderMAIN start ", end=" ")
    for key in KEYLIST:
        for d in MA_LIST:
            scale = int((a / (len(MA_LIST) * len(KEYLIST))) * 100)
            if scale % 5 == 0 and prescale != scale:
                print("*", end="")
            a += 1
            prescale = scale
            a += 1
            # 生成MA
            cat = key + "_ma_" + str(d)
            df.loc[:, cat] = df.loc[:, key].rolling(d).mean().round(2)

            if shift == True:
                df.loc[:, cat] = df.loc[:, cat].shift(1)
            # 生成均线布尔值
            df, NewName = A_B(df, cat, key)
            df, NewName = INTPresent(df, cat)
    print(
        " MAsBuliderMAIN done => {}s".format((datetime.datetime.now() - start).seconds)
    )
    return df


def Ma_order_count(
    df, day_list=[3, 5, 8, 13], key_list=["low", "open", "close", "high"]
):
    a = 0
    col_sort = []
    prescale = 0
    start = datetime.datetime.now()
    print("Ma_order_count start ", end=" ")
    for key in key_list:
        colNames = []
        for ran in day_list:
            scale = int((a / (len(key_list) * len(day_list))) * 100)
            if scale % 5 == 0 and prescale != scale:
                print("*", end="")
            a += 1
            prescale = scale
            col = "{}_ma_{}".format(key, ran)
            colNames.append(col)

        colNames.insert(0, "date")
        colNames.append("count")
        temp = df[colNames]
        colNames.remove("date")
        dataL = []
        for ran in range(df.shape[0]):
            indTemp = temp.iloc[ran]
            if indTemp["count"] > 1:
                num_down = 0  # 计数器
                num_up = 0  # 计数器
                for ind in range(len(colNames) - 2):
                    c1 = indTemp.loc[colNames[ind]]
                    c2 = indTemp.loc[colNames[ind + 1]]
                    if c1 > c2 and num_up == 0:
                        num_down += c1
                    elif c1 < c2 and num_down == 0:
                        num_up -= c1

                tempd = {}
                tempd["date"] = indTemp.loc["date"]
                col = key + "_sort"
                num = (num_down + num_up) / int(indTemp["count"] - 1)
                tempd[col] = round(num, 3)
                dataL.append(tempd)
                col_sort.append(col)
        gc.collect()
        dft = pd.DataFrame(dataL)
        dft.drop_duplicates("date", keep="first", inplace=True)
        if dft.shape[0] > 0:
            dft = dft[["date", col]]
            dft = dft.fillna(0)
        df = pd.merge(df, dft, on="date")
        gc.collect()
    col_sort = list(set(col_sort))
    df["All_sort"] = df[col_sort].sum(axis=1) / len(key_list)
    # df.drop(col_sort, axis=1, inplace=True)
    print(
        " Ma_order_count done=> {}s".format((datetime.datetime.now() - start).seconds)
    )
    return df


# def close_high(colName, df, colName2, boll=True):  # 计算开盘收盘均线差值
#     """
#     - colname 列1
#     - df 数据
#     - colname 列2
#     - boll 是否计算布尔值
#     - A - B值的计算，并且添加布尔值
#     """
#     Dnumber = colName.split("_")
#     colN2 = colName2 + "_ma_" + Dnumber[-1]
#     cat = Dnumber[0] + "-" + colName2 + "_ma_" + Dnumber[-1]
#     df.loc[:, cat] = df.loc[:, colName] - df.loc[:, colN2]
#     if boll == True:
#         df, newname = direction(df, cat)
#     # print(cat, ":", colName, ":", colN2)
#     return df


# def NewLines(namelist, df):  # 线名称判断
#     """
#     - 添加开盘-收盘
#     - 最高-最低
#     - namelist = 文件名称
#     - df = df
#     """
#     for colName in namelist:
#         if (
#             "ma" in colName
#             and "-" not in colName
#             and "%" not in colName
#             and "Boll" not in colName
#             and "by" not in colName
#         ):
#             if "close" in colName:
#                 df = close_high(colName, df, "open", boll=True)

#             if "high" in colName:
#                 df = close_high(colName, df, "low", boll=False)
#     return df


# def High_Low_Main(Ma_day_list, df, start):  # 函数计算移动平均线的差值，例如开盘-收盘...
#     """
#     + Ma_day_list, 移动平均线的数值列表
#     + t， 分割点位置
#     + df pd.DataFrame
#     """

#     def dfrebuilder(df, Ma_day_list=[], key="high-low_ma_"):
#         dlist = Ma_day_list.copy()
#         dlist.sort()
#         keylist_key = []
#         for num in dlist:
#             name = key + str(num)
#             keylist_key.append(name)
#         keylist_key.insert(0, "date")
#         keylist_key.append("count")
#         df = df[keylist_key]

#         return df

#     # 循环计算
#     def High_Low_Merge(
#         how_df,
#         start=2,
#         revers=True,
#     ):
#         """
#         计算高低顺序，总程序
#         """

#         dic = []
#         colN = how_df.columns.tolist()[1:-1]
#         key = re.findall("(.*)\d", colN[0])[0]
#         for ind in df.index.tolist():
#             temp = how_df.iloc[ind]
#             if temp["count"] > 1:
#                 num_sell = 0  # 计数器
#                 num_buy = 0  # 计数器
#                 for rang in range(len(colN) - 1):
#                     c1 = temp.loc[colN[rang]]
#                     c2 = temp.loc[colN[rang + 1]]
#                     if c1 > c2 and num_buy == 0:
#                         num_sell += -1
#                     elif c1 < c2 and num_sell == 0:
#                         num_buy += 1
#                     else:
#                         break

#                 tempd = {}
#                 tempd["date"] = temp.loc["date"]
#                 col = key + "Main"
#                 num = (num_sell + num_buy) / int(temp["count"] - 1)
#                 tempd[col] = round(num, 3)
#                 dic.append(tempd)

#         dft = pd.DataFrame(dic)
#         dft.drop_duplicates("date", keep="first", inplace=True)

#         if dft.shape[0] > 0:
#             dft = dft[["date", col]]
#             if revers == True:
#                 dft.loc[:, col] = -dft.loc[:, col]
#             dft = dft.fillna(0)
#             gc.collect()
#             return dft
#         else:
#             return pd.DataFrame()

#     df = df.drop_duplicates("date")
#     high_low_ma_df = dfrebuilder(df, Ma_day_list=Ma_day_list, key="high-low_ma_")
#     high_low_ma_df = High_Low_Merge(
#         high_low_ma_df,
#         start=start,
#         revers=True,
#     )
#     gc.collect()
#     close_open_ma_df = dfrebuilder(df, Ma_day_list=Ma_day_list, key="close-open_ma_")
#     close_open_ma_df = High_Low_Merge(
#         close_open_ma_df,
#         start=start,
#         revers=True,
#     )
#     gc.collect()
#     df = pd.merge(df, high_low_ma_df, how="left", on="date")
#     df = pd.merge(df, close_open_ma_df, how="left", on="date")
#     df = df.drop_duplicates("date")
#     return df


def SortListbyType(col_lists):  # 按照类型排序
    """
    对符号进行排序
    """

    for _ in range(2):
        bolls = []
        present = []

        for col in col_lists:
            m = []
            if "%" in col:
                col_lists.remove(col)
                if "-" in col:
                    present.append(col)
                else:
                    m.append(col)
            elif "Boll" in col:
                col_lists.remove(col)
                if "-" in col:
                    bolls.append(col)
                else:
                    m.append(col)
            # bolls.sort()
            # present.sort()
            bolls.extend(m)

        col_lists.extend(bolls + present)

    return list(col_lists)


# 对布尔值进行加总，并且删除完成计算的值，注意不要把sell和buy也删了
# def BollSams(df, keyls, main_key, Ma_Day_List=[3, 5, 8], sub_Key="Boll", drop=True):

#     for key in keyls:
#         if key == "close":
#             filters = "open"
#         elif key == "open":
#             filters = "close"
#         elif key == "low":
#             filters = "high"
#         elif key == "high":
#             filters = "low"
#         temp_list = []

#         for colNumber in range(df.shape[0]):
#             # dic = {}  # "date", df.date.iloc[colNumber]
#             l = []
#             count = 0
#             for num in Ma_Day_List:
#                 cat = key + "_ma_" + str(num)
#                 if df[cat].iloc[colNumber] > 0:
#                     count += 1

#             for col in df.columns.to_list():
#                 if key in col and main_key in col:
#                     if sub_Key:
#                         if sub_Key in col:
#                             l.append(df.loc[colNumber, col])
#                             if drop == True:
#                                 df = df.drop(col, axis=1)
#                     else:
#                         l.append(df.loc[colNumber, col])
#                         if drop == True:
#                             df = df.drop(col, axis=1)
#             if sub_Key:
#                 cat = key + main_key + sub_Key + "_sum"
#             else:
#                 cat = key + main_key + "_sum"
#             if count != 0:
#                 dic = {
#                     "date": df.date.iloc[colNumber],
#                     cat: round(sum(l) / count, 2),
#                 }
#                 temp_list.append(dic)
#         _df = pd.DataFrame(temp_list)
#         df = pd.merge(df, _df, on="date")
#         # print(df[cat])
#         return df


# def BollFastSum(
#     _df, keyls, main_key, Ma_Day_List=[3, 5, 8], sub_Key="Boll", drop=False
# ):
#     """
#     df = pd.DataFrame
#     main_key = 主筛选词汇
#     sub_key = 辅助词汇
#     """
#     colL = _df.columns.to_list()
#     for key in keyls:
#         if key == "close":
#             filters = "open"
#         elif key == "open":
#             filters = "close"
#         elif key == "low":
#             filters = "high"
#         elif key == "high":
#             filters = "low"
#         temp_list = []
#         for col in colL:
#             if (
#                 key in col
#                 and main_key in col
#                 and filters not in col
#                 and "std" not in col
#             ):
#                 if sub_Key != "" or sub_Key != None:
#                     if sub_Key in col:
#                         temp_list.append(col)
#                         if drop == True:
#                             _df = _df.drop(col, axis=1)

#                 else:
#                     if "Boll" not in col:
#                         temp_list.append(col)
#                         if drop == True:
#                             _df = _df.drop(col, axis=1)
#         key = key + main_key + sub_Key + "_sum"
#         _df[key] = _df[temp_list].sum(axis=1)
#         _df[key] = _df[key] / _df["count"]

#     return _df


def BollFastSum_All(
    _df, keyls, main_key, Ma_Day_List=[3, 5, 8], sub_Key="Boll", drop=False
):
    """
    df = pd.DataFrame
    main_key = 主筛选词汇
    sub_key = 辅助词汇
    """
    colL = _df.columns.to_list()

    temp_list = []
    for col in colL:
        for key in keyls:
            # if key == "close":
            #     filters = "open"
            # elif key == "open":
            #     filters = "close"
            # elif key == "low":
            #     filters = "high"
            # elif key == "high":
            #     filters = "low"
            # else:
            #     filters = ""

            if (
                key in col
                and main_key in col
                # and filters not in col
                and "_sum" not in col
                and "std" not in col
            ):
                if sub_Key != "" and sub_Key != None:
                    if sub_Key in col:
                        temp_list.append(col)
                        if drop == True:
                            _df = _df.drop(col, axis=1)
                else:
                    if "Boll" not in col:
                        temp_list.append(col)
                        if drop == True:
                            _df = _df.drop(col, axis=1)
    key = "All" + main_key + sub_Key + "_sum"
    _df[key] = _df[temp_list].sum(axis=1)
    _df[key] = _df[key] / _df["count"] / len(keyls)

    return _df


# def SellSignal(df, ma_day_list, startday=2, line_Pares=1):  # 抛售信号
#     # namelist = SortListbyType(df.columns.tolist()) # 按照类型分类整理
#     # df = df.loc[:, namelist] # 重新赋值表格
#     namelist = df.columns.tolist()  # 替换原来的namlist
#     df = NewLines(namelist, df)  # 绘制新的计算线，计算开盘收盘差值，最高最低差值
#     Ma_day_l = ma_day_list  # 对 WAVES.WAVES(NDAY) 进行切片
#     Ma_day_l.sort()  # 按照数值大小排列
#     # for d in range(nday - start):  # 不知道为什么循环在下一级失效，只能拿出来了
#     t = startday * line_Pares

#     df = High_Low_Main(Ma_day_l, df, t)  # 计算差值之间的布尔值
#     return df


def resoult(df, code, AvableCash):
    dlist = pd.to_datetime(df.loc[:, "date"]).tolist()
    years = int(str(dlist[-1] - dlist[0]).split(" ")[0]) / 365
    finalValue = df.iloc[-2]["TotalValues"]
    persent = round(((df.iloc[-2]["TotalValues"] / AvableCash) ** (1 / years) - 1), 5)
    incraceRate = df.iloc[-2]["TotalValues"] / AvableCash - 1
    maxmentChange = (
        df.loc[:, "TotalValues"].max() - df.loc[:, "TotalValues"].min()
    ) / AvableCash
    callBacks = df.loc[:, "TotalValues"].min() / AvableCash
    dic = {
        "code": "'" + str(code),
        "period/year": round(years, 2),
        "finalValue": round(finalValue, 2),
        "%_pre_year": round(persent, 5),
        "%_increase": round(incraceRate, 5),
        "Change_Rate": round(maxmentChange, 5),
        "Maxmut_Loss": round(callBacks, 5),
    }
    return dic


def Waves(n):
    """
    获取波浪天数均线
    n = 线的组数
    """
    Buy, Sell, Total = [], [], []
    main, side = 5, 3
    total = main + side
    Total.append(total)
    Buy.append(main)
    Sell.append(side)
    for i in range(n):
        buy = main * 3 + side * 2
        sell = main * 2 + side
        total = buy + sell
        main, side = buy, sell
        Buy.append(buy)
        Sell.append(sell)
        Total.append(total)

    DC = []

    DC.extend(Buy)
    DC.extend(Sell)
    DC.extend(Total)

    return DC


def fibonacci(r):
    r = r * 3
    lists = []
    a = 1
    b = 0
    for i in range(r):
        c = a + b
        b = a
        a = c
        print(a)
        lists.append(a)


# def highlow_sortter(lists):
#     # print(lists)
#     if len(lists) % 2 == 0:
#         cutF = int(len(lists) / 2)
#         x = [lists[:cutF], lists[cutF:]]
#     else:
#         cutF = int(len(lists) / 2)
#         x = [lists[:cutF], lists[cutF:]]
#     while True:
#         b = []
#         for a in x:
#             if len(a) > 1:
#                 min_ind = a.index(min(a))
#                 max_ind = a.index(max(a))
#                 if (
#                     (min_ind == 0 and max_ind == len(a))
#                     or (max_ind == 0 and min_ind == len(a))
#                     or min_ind == max_ind
#                 ):

#                     b.append(a)
#                     continue
#                 elif (
#                     min_ind == 0
#                     or max_ind == 0
#                     or max_ind == len(a)
#                     or min_ind == len(a)
#                 ):
#                     if min_ind == 0 or max_ind == 0:
#                         if min_ind == 0:
#                             cutter = max_ind
#                         elif max_ind == 0:
#                             cutter = min_ind
#                         l2 = a[cutter + 1 :]
#                         l1 = a[: cutter + 1]
#                         nl = [l1, l2]

#                     elif max_ind == len(a) or min_ind == len(a):
#                         if min_ind == len(a):
#                             cutter = max_ind
#                         elif max_ind == len(a):
#                             cutter = min_ind
#                         l1 = a[cutter + 1 :]
#                         l2 = a[: cutter + 1]
#                         nl = [l1, l2]

#                 else:
#                     if min_ind > max_ind:
#                         l3 = a[min_ind:]
#                         l2 = a[max_ind:min_ind]
#                         l1 = a[:max_ind]

#                     elif min_ind < max_ind:
#                         l3 = a[max_ind:]
#                         l2 = a[min_ind:max_ind]
#                         l1 = a[:min_ind]

#                     nl = [l1, l2, l3]

#                 nl2 = []
#                 for c in nl:
#                     if len(nl[0]) == 1 and nl.index(c) == 0:
#                         nl2.append(c)
#                     elif len(c) == 1:
#                         nl2[-1].extend(c)
#                     elif c != []:
#                         nl2.append(c)
#                 b.extend(nl2)
#             else:
#                 b.append(a)
#         if x == b:
#             break
#         x = b
#     return x
#     # for _ in x:
#     #     print(_)